#include "eventloop.hpp"
#include "acceptor.hpp"
#include "tcpconnection.hpp"
namespace moqi
{
using std::runtime_error;

EventLoop::EventLoop(Acceptor & acceptor, int max_events,int initSec, int peridoSec):
_epollfd(createEpollFd()),
_eventfd(createEventFd()),
_timefd(createTimeFd(initSec, peridoSec)),
_max_events(max_events),
_isLooping(false),
_mutex(),
_acceptor(acceptor)
{

    add(_acceptor.getSockFd());
    add(_eventfd);
    add(_timefd);
    _events.resize(max_events);
}

EventLoop::~EventLoop()
{
    if(_epollfd > 0)
    {
        close(_epollfd);
    }
    if(_eventfd > 0)
    {
        close(_eventfd);
    }
    LOG_INFO("~EventLoop");
}

void EventLoop::loop()
{
    _isLooping = true;
    LOG_INFO("loop running");
    while(_isLooping)
    {
        waitEvent();
    }
}

void EventLoop::unloop()
{
    LOG_INFO("loop stop");
    _isLooping = false;
}

void EventLoop::sendInLoop(Functor &&cb)
{
    LOG_INFO("EventLoop::sendInLoop");
    {
        MutexLockGuard autolock(_mutex);
        _sendReplays.push_back(std::move(cb));//待发送回复的任务列表
    }
    wakeup();
}

void EventLoop::waitEvent(int timeoutMs)
{
    int nready = epoll_wait(_epollfd, _events.data(), _events.size(), timeoutMs);
    if(nready == -1)
    {
        if(errno == EINTR)
        {
            return ;
        }
        LOG_ERROR("Error in epoll_wait");
        throw runtime_error("Error in epoll_wait");
    }
    else if(nready == 0)
    {
        LOG_INFO("epoll_wait timeout");
    }
    else
    {

        for(int i = 0; i < nready; ++i)
        {
            int fd = _events[i].data.fd;
            if(fd == _acceptor.getSockFd())
            {
                handleConnection();//处理新链接
            }
            else if (fd == _eventfd)
            {
                handleread(fd);//处理线程通知事件，也就是消息处理结束
                sendReplays();
            }
            else if (fd == _timefd)
            {
                handleread(fd);//处理线程通知事件，也就是定时器到期
                handleTimeOut();//处理定时器超时事件
            }
            else
            {
                handleMessage(fd);//处理消息到来
            }
        }
    }
}

void EventLoop::wakeup()
{
    LOG_INFO("wakeup");
    uint64_t one = 1;
    int ret = write(_eventfd, &one, sizeof(one));
    if(ret != sizeof(one)) 
    {
        LOG_ERROR("write");
    }
}

void EventLoop::handleread(int fd)
{
    LOG_INFO("handlread");
    uint64_t one = 0;
    int ret = read(fd, &one, sizeof(one));
    if(ret != sizeof(one)) 
    {
        LOG_ERROR("read");
    }
}

void EventLoop::add(int fd, uint32_t Epoll_events)
{
    LOG_INFO("addEpollfd");
    event_t event;//待监听事件
    memset(&event, 0, sizeof(event));
    event.events = Epoll_events;//默认参数为EPOLLIiN
    event.data.fd = fd;//待监听套接字                        
    if(epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &event) == -1)
    {
        LOG_ERROR("Fail to add fd to epoll"); 
        throw runtime_error("Fail to add fd to epoll");        
    }
}

void EventLoop::del(int fd)
{
    LOG_INFO("delEpollfd");
    if(epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, nullptr) == -1)
    {
        LOG_ERROR("Fail to del fd to epoll");        
        throw runtime_error("Fail to del fd to epoll");        
    }
}

void EventLoop::mod(int fd, uint32_t Epoll_events)
{
    event_t event;//待监听事件
    memset(&event, 0, sizeof(event));
    event.events = Epoll_events;//默认参数为EPOLLIiN
    event.data.fd = fd;//待监听套接字                        
    if(epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &event) == -1)
    {
        LOG_ERROR("Fail to mod fd to epoll");        
        throw runtime_error("Fail to mod fd to epoll");        
    }
}

void EventLoop::handleConnection()
{
    LOG_INFO("handleConnection");
    int fd = _acceptor.accept();//接收新链接
    add(fd);//将新链接加入监听
    TcpConnectionPtr pconnect(new TcpConnection(fd,this));//创建TcpConnection智能指针
    pconnect->setAllCallback(_onConnection, _onMessage, _onClose);//为其设置任务处理函数
    //_connects.insert(std::make_pair(fd, pconnect));//添加fd和TcpConnection的映射
    _connects[fd] = pconnect;
    pconnect->handleConnectionCallback();//Tcpconnection处理新链接
}

void EventLoop::handleTimeOut()
{
    auto it = _connects.begin();
    while(it!=_connects.end())
    {
        if(it->second->checkTimeOut(10))
        {
            LOG_INFO(it->second->status()+"longtime inactive! kick it!");
            del(it->first);//删除监听
            it = _connects.erase(it);
        }
        else
        {
            ++it;
        }
    }
}

void EventLoop::handleMessage(int fd)
{
    LOG_INFO("handleMessage");
    auto it = _connects.find(fd);
    if(it != _connects.end())
    {
        if(it->second->isClosed())
        {
            it->second->handleCloseCallback();//TcpConnection的断开处理
            del(fd);//epoll的监听删除处理
            _connects.erase(fd);//fd和tcp映射的删除处理
        }
        else
        {
            it->second->updateActiveTime();//用来更新时间
            it->second->handleMessageCallback();
        }
    }
}
void EventLoop::sendReplays()
{
    LOG_INFO(">> sendReplays");
    vector<Functor> tmp;
    {
        MutexLockGuard autolock(_mutex);
        tmp.swap(_sendReplays);//O(1)
    }
    //其他的计算线程可以对他进行修改操作
    for(auto & f : tmp) {
        f();
    }
}
int EventLoop::createEpollFd()
{
    int fd = epoll_create1(0);
    if(fd == -1)
    {
        LOG_ERROR("Failed to create epoll instance");
        throw runtime_error("Failed to create epoll instance");
    }
    return fd;
}

int EventLoop::createEventFd()
{
    int fd = eventfd(0, 0);
    if(fd < 0) {
        LOG_ERROR("eventfd");
    }
    return fd;
}

int EventLoop::createTimeFd(int initSec, int peridoSec)
{
    int fd = timerfd_create(CLOCK_MONOTONIC, 0); 
    if(fd < 0)
    {   
        LOG_ERROR("createTimerFd");
        throw runtime_error("Failed to create timefd");
        return -1; 
    }   

    struct itimerspec newtimet;
    newtimet.it_value.tv_sec = initSec;//起始时间，秒
    newtimet.it_value.tv_nsec = 0;//起始时间，纳秒

    newtimet.it_interval.tv_sec = peridoSec;//周期
    newtimet.it_interval.tv_nsec = 0;
    int ret = timerfd_settime(fd, 0, &newtimet, nullptr);
    if(ret < 0)
    {
        LOG_ERROR("TimerFd_settime");
        throw runtime_error("Failed to settime");
    }
    return fd; 
}

}//end of namespace moqi
